#ifndef __TrianleH__
#define __TrianleH__

#include "Vectors.h"
#include "Material.h"

class KDTree;
struct AABB
{
	float minXYZ[3];
	float maxXYZ[3];
	AABB(){
		minXYZ[0] = minXYZ[1] = minXYZ[2] = FLT_MAX;
		maxXYZ[0] = maxXYZ[1] = maxXYZ[2] = -FLT_MAX;
	}
	float getVolume(){
		float a,b,c;
		a = maxXYZ[0] - minXYZ[0];
		b = maxXYZ[1] - minXYZ[1];
		c = maxXYZ[2] - minXYZ[2];
		return a*b*c;
	}
	float getArea(){
		float a,b,c;
		a = maxXYZ[0] - minXYZ[0];
		b = maxXYZ[1] - minXYZ[1];
		c = maxXYZ[2] - minXYZ[2];
		return 2*(a*b+b*c+a*c);
	}
	void updateByVec3f(const Vec3f &point){
		if ( point[0] < minXYZ[0] ) minXYZ[0] = point[0];
		if ( point[1] < minXYZ[1] ) minXYZ[1] = point[1];
		if ( point[2] < minXYZ[2] ) minXYZ[2] = point[2];
		if ( point[0] > maxXYZ[0] ) maxXYZ[0] = point[0];
		if ( point[1] > maxXYZ[1] ) maxXYZ[1] = point[1];
		if ( point[2] > maxXYZ[2] ) maxXYZ[2] = point[2];
	}
	void updateByAABB(const AABB &aabb){
		if ( aabb.minXYZ[0] < minXYZ[0] ) minXYZ[0] = aabb.minXYZ[0];
		if ( aabb.minXYZ[1] < minXYZ[1] ) minXYZ[1] = aabb.minXYZ[1];
		if ( aabb.minXYZ[2] < minXYZ[2] ) minXYZ[2] = aabb.minXYZ[2];
		if ( aabb.maxXYZ[0] > maxXYZ[0] ) maxXYZ[0] = aabb.maxXYZ[0];
		if ( aabb.maxXYZ[1] > maxXYZ[1] ) maxXYZ[1] = aabb.maxXYZ[1];
		if ( aabb.maxXYZ[2] > maxXYZ[2] ) maxXYZ[2] = aabb.maxXYZ[2];
	}
	void intersect(const Ray &ray,float &tmin,float &tmax) const{
		Vec3f R = ray.getOrigin(); Vec3f D = ray.getDirection();
		tmin = FLT_MAX; tmax = -FLT_MAX;
		if ( D.x()>=FLT_EPSILON || D.x()<=-FLT_EPSILON ){
			float t1 = (minXYZ[0]-R[0]) / D[0];
			float t2 = (maxXYZ[0]-R[0]) / D[0];
			if ( t1 < t2 ){
				if ( t1 < tmin ) tmin = t1; if( t2 > tmax ) tmax = t2;
			} else {
				if ( t2 < tmin ) tmin = t2; if( t1 > tmax ) tmax = t1;
			}
		}
		if ( D.y()>=FLT_EPSILON || D.y()<=-FLT_EPSILON ){
			float t1 = (minXYZ[1]-R.y()) / D.y();
			float t2 = (maxXYZ[1]-R.y()) / D.y();
			if ( t1 < t2 ){
				if ( t1 < tmin ) tmin = t1; if( t2 > tmax ) tmax = t2;
			} else {
				if ( t2 < tmin ) tmin = t2; if( t1 > tmax ) tmax = t1;
			}
		}
		if ( D.z()>FLT_EPSILON || D.z()<-FLT_EPSILON ){
			float t1 = (minXYZ[2]-R.z()) / D.z();
			float t2 = (maxXYZ[2]-R.z()) / D.z();
			if ( t1 < t2 ){
				if ( t1 < tmin ) tmin = t1; if( t2 > tmax ) tmax = t2;
			} else {
				if ( t2 < tmin ) tmin = t2; if( t1 > tmax ) tmax = t1;
			}
		}
	}
};

class Triangle
{
	friend KDTree;
public:
	Triangle(Vec3f v0, Vec3f v1, Vec3f v2, Material *pMaterial);
	Triangle(Vec3f v[3],Material *pMaterial);
	virtual bool intersect(const Ray *pRay, Hit *pHit, float tmin, float tmax);
	bool intersectShadowRay(const Ray *pRay, float tmin, float tmax);
	Material *getMatrial() const {return m_pMaterial;}
	void debugMessage();
	const Vec3f & getV0() {return m_v0;}
	const Vec3f & getV1() {return m_v1;}
	const Vec3f & getV2() {return m_v2;}
protected:
	Vec3f m_v0,m_v1,m_v2;
	Vec3f m_normal;
	Material *m_pMaterial;
	AABB m_AABB;
};

class NormalTriangle : public Triangle
{
	friend KDTree;
public:
	NormalTriangle(Vec3f v0,Vec3f v1,Vec3f v2,Vec3f n0,Vec3f n1,Vec3f n2,
		Material *pMaterial):Triangle(v0,v1,v2,pMaterial),m_n0(n0),
		m_n1(n1),m_n2(n2){}
	NormalTriangle(Vec3f v[], Vec3f n[], Material *pMaterial):
		Triangle(v,pMaterial),m_n0(n[0]),m_n1(n[1]),m_n2(n[2]){}
	bool intersect(const Ray *pRay, Hit *pHit, float tmin, float tmax);
private:
	Vec3f m_n0,m_n1,m_n2;
};

class TextureTriangle : public Triangle
{
	friend KDTree;
public:
	TextureTriangle(Vec3f v0,Vec3f v1,Vec3f v2,Vec2f tex0,Vec2f tex1,Vec2f tex2,
		Vec3f n0,Vec3f n1,Vec3f n2,Material *pMaterial)
		: Triangle(v0,v1,v2,pMaterial),m_tex0(tex0),m_tex1(tex1),m_tex2(tex2){
			m_n0 = n0; m_n1 = n1; m_n2 = n2;
			
	}
	TextureTriangle(Vec3f v[3],Vec3f n[3],Vec2f t[3],Material *pMaterial)
		: Triangle(v,pMaterial),m_tex0(t[0]),m_tex1(t[1]),m_tex2(t[2]),
		m_n0(n[0]),m_n1(n[1]),m_n2(n[2]){}
	TextureTriangle(Vec3f v[3],Vec2f t[3],Material *pMaterial)
		: Triangle(v,pMaterial),
		m_tex0(t[0]),m_tex1(t[1]),m_tex2(t[2]){
		m_n0 = m_n1 = m_n2 = m_normal;
	}
 	bool intersect(const Ray *pRay, Hit *pHit, float tmin, float tmax);
// 	bool intersectShadowRay(const Ray *pRay, float tmin, float tmax);
// 	void debugMessage();
// 	const Vec3f & getV0() {return m_v0;}
// 	const Vec3f & getV1() {return m_v1;}
// 	const Vec3f & getV2() {return m_v2;}
private:
	Vec2f m_tex0,m_tex1,m_tex2;
	Vec3f m_n0,m_n1,m_n2;
};
#endif